package com.zbkj.admin.service;

import cn.hutool.extra.spring.SpringUtil;
import com.google.common.collect.Lists;
import com.mybatisflex.core.query.QueryMethods;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.core.service.IService;
import com.zbkj.admin.pojo.*;
import com.zbkj.admin.pojo.table.Tables;
import lombok.val;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public interface EbUserRelationshipService extends IService<EbUserRelationshipEntity>, BaseService<EbUserRelationshipEntity> {

    /**
     * 获取用户关系树(树中搜索 - 获取匹配节点及其父级、祖父级（向上三级平坦数据）)
     *
     * @param userId 用户id
     * @return 用户关系树
     */
    @SuppressWarnings("all")
    default List<EbUserRelationshipEntity> threeLevelsUpward(Long userId, Integer currentDepth) {
        List<EbUserRelationshipEntity> list = list(
                from().as("ur")
                        .select(Tables.ebUserRelationshipEntity.defaultColumns, Tables.ebUserEntity.defaultColumns)
                        .leftJoin(EbUserEntity.class).as("u")
                        .on(EbUserEntity::getId, EbUserRelationshipEntity::getUid)
//                        .where(EbUserRelationshipEntity::getIsEstablish).eq(1)
        );
        if (Objects.nonNull(currentDepth)) {
            List<List<EbUserRelationshipEntity>> partition = Lists.partition(list, 280);
            Map<String, Object> map = new HashMap<>();
            Map<String, BigDecimal> userData = new HashMap<>();
            Map<String, BigDecimal> userPointsData = new HashMap<>();
            IEbUserIntegralProductRecordService productRecordService = SpringUtil.getBean(IEbUserIntegralProductRecordService.class);
            partition.forEach(it -> {
                it.forEach(record -> {
                    var serviceObjAs = productRecordService.getObjAs(from(EbUserIntegralProductRecordEntity.class)
                                    .select(QueryMethods.sum(EbUserIntegralProductRecordEntity::getIntegral))
                                    .where(EbUserIntegralProductRecordEntity::getUid).eq(record.getUid())
                                    .eq(EbUserIntegralProductRecordEntity::getStatus, 2)
                                    .eq(EbUserIntegralProductRecordEntity::getType, 3)
                                    .eq(EbUserIntegralProductRecordEntity::getDataStatus, 0)
                            , BigDecimal.class);
                    BigDecimal objAs = getObjAs(from(EbOrderEntity.class)
                                    .select(QueryMethods.sum(EbOrderEntity::getPayPrice))
                                    .where(EbOrderEntity::getUid).eq(record.getUid())
                                    .and(EbOrderEntity::getStatus).ge(1)
                                    .and(EbOrderEntity::getPayTime).isNotNull()
                                    .and(EbOrderEntity::getRefundStatus).eq(0)
                                    .and(EbOrderEntity::getCancelStatus).eq(0)
                                    .eq(EbOrderEntity::getMerId, 0)
                            , BigDecimal.class);
                    userPointsData.put(String.valueOf(record.getUid()), serviceObjAs);
                    userData.put(String.valueOf(record.getUid()), objAs);
                });
            });
            Map<String, List<Map.Entry<String, Object>>> collect = map.entrySet().parallelStream().collect(Collectors.groupingBy(Map.Entry::getKey));
            list.forEach(x -> {
                x.setPoints(userPointsData.get(String.valueOf(x.getUid())));
                x.setTurnover(userData.get(String.valueOf(x.getUid())));
//                String key = String.valueOf(map.get("uid"));
//                if (Objects.nonNull(key)) {
//                    if (String.valueOf(x.getUid()).equalsIgnoreCase(key)) {
//                        x.setTurnover((BigDecimal) map.get("turnover"));
//                    } else {
//                        x.setTurnover(BigDecimal.ZERO);
//                    }
//                }
            });
        }
        list = list.stream().collect(Collectors.collectingAndThen(Collectors.
                toCollection(() -> new TreeSet<>(Comparator.comparing(EbUserRelationshipEntity::getUid))), ArrayList::new));
        List<EbUserRelationshipEntity> ebUserRelationshipEntities = makeTree(list, x -> x.getPId() == 0L, (x, y) -> x.getUid().equals(y.getPId()),
                EbUserRelationshipEntity::setSubset);
        List<EbUserRelationshipEntity> finalUse = list;
        List<EbUserRelationshipEntity> userRelationshipEntities = searchThreeLevelsUpward(ebUserRelationshipEntities,
                x -> x.getUid().equals(userId),
                EbUserRelationshipEntity::getSubset,
                node -> finalUse.stream().parallel()
                        .filter(n -> n.getUid().equals(node.getPId())).findFirst().orElse(null)
                , currentDepth);
        return userRelationshipEntities;
    }

    /**
     * 创建树结构
     *
     * @param list           原始数据
     * @param rootCheck      x->x.getPId()==-1L
     * @param parentCheck    (x, y)->x.getId().equals(y.getPId())
     * @param setSubChildren MenuVo::setSubMenus
     * @param <E>            数据类型
     */
    static <E> List<E> makeTree(List<E> list, Predicate<E> rootCheck, BiFunction<E, E, Boolean> parentCheck, BiConsumer<E, List<E>> setSubChildren) {
        return list.stream().parallel().filter(rootCheck).peek(x -> setSubChildren.accept(x, makeChildren(x, list, parentCheck, setSubChildren)))
                .collect(Collectors.toList());
    }

    private static <E> List<E> makeChildren(E parent, List<E> allData, BiFunction<E, E, Boolean> parentCheck, BiConsumer<E, List<E>> setSubChildren) {
        return allData.stream().parallel().filter(x -> parentCheck.apply(parent, x)).peek(x -> setSubChildren.accept(x, makeChildren(x, allData, parentCheck, setSubChildren))).collect(Collectors.toList());
    }

    /**
     * 树中搜索 - 获取匹配节点及其父级、祖父级（向上三级平坦数据）
     *
     * @param tree      原始树结构
     * @param predicate 匹配条件
     * @param getParent 获取父节点的方法（需实现）
     * @param <E>       泛型类型
     * @return 返回包含匹配节点及其向上两级父节点的列表
     */
    static <E> List<E> searchThreeLevelsUpward(List<E> tree,
                                               Predicate<E> predicate,
                                               Function<E, List<E>> getSubChildren,
                                               Function<E, E> getParent,
                                               int maxDepth) {
        return tree.stream().parallel()
                .flatMap(node -> flattenTree(node, getSubChildren).parallelStream())
                .filter(predicate).map(matchedNode -> collectThreeLevelsUpward(matchedNode, getParent, maxDepth))
                .flatMap(Collection::parallelStream).distinct().collect(Collectors.toList());
    }

    /**
     * 向上收集三级节点（祖父级 -> 父级 -> 当前节点）
     */
    private static <E> List<E> collectThreeLevelsUpward(E node, Function<E, E> getParent, int maxDepth) {
        List<E> path = new ArrayList<>();

        // 当前节点
        path.add(node);
//        int depth = 0;
//
//        while (node != null && depth < maxDepth) {
//            path.add(node);
//            node = getParent.apply(node);
//            depth++;
//        }

//        while (node != null && depth < maxDepth) {
//            // 父级
//            E parent = getParent.apply(node);
//            if (parent != null) {
//                path.add(parent);
//
//                // 祖父级
//                E grandParent = getParent.apply(parent);
//                if (grandParent != null) {
//                    path.add(grandParent);
//                }
//            }
//        }
        return path;
    }

    /**
     * 展平树结构（用于查找所有节点）
     */
    @SuppressWarnings("all")
    private static <E> List<E> flattenTree(E node, Function<E, List<E>> getSubChildren) {
        List<E> result = new ArrayList<>();
        result.add(node);
        List<E> children = getSubChildren.apply(node);
        if (children != null) {
            children.stream().parallel().peek(child -> result.addAll(flattenTree(child, getSubChildren))).toList();
        }
        return result;
    }

}
